Формат объектного файла.

Объектный файл имеет теговую структуру.
    Номинально, порядок блоков не имеет значения. Однако программа создаёт
объектный файл, в котором блоки располагаются в следующей последовательности:
1. Блок идентификатор
2. Блок данных
3. Блок глобальных меток
4. Блок локальных меток (если есть)
5. Блок ссылок на метки

Каждый блок имеет заголовок следующего формата:
{
    OBJTags  nHdrTag;       // Тэг заголовка
    OBJTags  nBlockTag;     // Тэг блока
    uint32_t nBlkLen;       // Размер данных блока в байтах (не включая размер заголовка)
    uint32_t nEntryCount;   // Количество элементов в блоке
    uint32_t nCheckSum;     // Контрольная сумма блока (на данный момент не
                            // используется и == 0)
}

Тэг заголовка
    Это константное значение, по которому определяется, что это действительно
заголовок и остальные поля имеют смысл. Оно одинаково для всех блоков.

Тэг блока
    Константное значение, по которому определяется тип блока. И как
интерпретировать данные блока.

Размер данных блока
    Размер данных блока в байтах, следующих следом за заголовком.

Количество элементов в блоке
    Количество элементов данных блока. Например, количество меток в таблице.

Контрольная сумма блока
    Не используется, должна быть 0. Может быть, когда-нибудь потом, будет
использоваться.


    За заголовком блока следуют данные. В каждом блоке они интерпретируются
по своему.

Блок идентификатор
    Имеет данные следующей структуры:
{
    uint32_t nVersion;      // Версия объектного файла
    int16_t nStartAddress;  // Адрес компоновки по умолчанию объектного модуля.
    int16_t nMode;          // Режим компоновки
}
    В заголовке этого блока - количество элементов == 1, а размер данных == 8 (на данный момент)

Версия объектного файла
    На данный момент - число 0x1001

Адрес компоновки по умолчанию объектного модуля
    По умолчанию, это число 01000. Или адрес, заданный псевдокомандой .LA, или
ключом -s.
    Этот адрес используется при линковке командой LI. Для первого объектного
файла, если адрес не задан явно ключом -s, берётся адрес компоновки из заголовка.
Для последующих объектных файлов - берётся текущий адрес компоновки, полученный
после компоновки предыдущего объектного файла, это как правило, адрес первого
слова за концом получающегося скомпонованного массива программы.

Режим компоновки
    Определяет стратегию обработки меток.
    Имеет три значения:
    -1 - объектный модуль скомпилирован в режиме CL, т.е. ещё не скомпонован и
полностью перемещаем.
    При линковке такого файла все метки корректируются в соответствии с базовым
адресом линковки модуля.
    1 и 0 - объектный модуль скомпилирован в режиме CO. Т.е. он скомпонован с
некоего определённого адреса и в него уже подставлены значения меток.
    При линковке такого файла, корректировать метки не имеет смысла, т.к. они
при этом станут некорректны. Поэтому они добавляются в результирующую таблицу
меток как есть.
    Вообще смысл линковки таких модулей имеет смысл либо, если этот модуль идёт
первым, либо, если в последствии массив данных этого модуля будет перемещён на
заданный адрес, с которого он был скомпонован, программой пользователя.


Блок данных
    Содержит собственно бинарный массив сформированного кода.
    В заголовке этого блока - количество элементов == 1, а размер данных - 
размер полученного кода.


Блок глобальных меток
    Содержит таблицу глобальных меток объектного модуля.
    В заголовке этого блока - количество элементов - количество меток, а размер
данных - размер всего массива таблицы меток в байтах.

Структура одного элемента метки:
{
    uint32_t OBJTags::OBJ_Label;    // Тэг элемента метки
    uint32_t len                    // Длина строки имени метки
    uint8_t[len] label_text;        // Строка имени метки в unicode
    uint32_t value                  // Значение метки
    uint32_t type                   // Тип метки
}

Тэг элемента метки
    Константное значение, по которому определяется, что далее следует верный
набор данных метки.

Длина строки имени метки
    Размер строки текста в знаках. Значение может быть от 1 до 2^32-1, т.е.
длина имени может достигать 2 Гб, думаю этого более, чем достаточно.

Строка имени метки в unicode
    Собственно имя метки. Строка не содержит заключающего нуля, т.к. перед ней
имеется размер строки.

Значение метки
    Это адрес, где метка была определена. Фактически - это смещение от начала
кода. И чтобы получить фактическое значение, к нему надо прибавить адрес
компоновки по умолчанию.

Тип метки
    Различается всего два вида глобальных меток: Метка и Константа (присваивание)
Они обрабатываются по разному. Метку можно релоцировать, константу - нет.
Так же в типе метки содержатся некоторые флаги операций над метками, это нужно
для арифметических операций над метками.


Блок локальных меток
    По своей структуре совершенно идентичен блоку глобальных меток. Вся разница
лишь в том, что при определении глобальной метки, вся таблица локальных меток
обнуляется. И в объектный файл записывается либо пустая таблица, либо остаточная
таблица на момент окончания компиляции. 


Блок ссылок на метки
    По своей структуре так же идентичен блоку определений меток, разница лишь в
том, что в поле "Значение метки" содержится относительный адрес, где встретилась
ссылка на заданную метку. А в поле "Тип метки" содержатся свои типы для ссылок.


................................................................................
далее - черновые наброски и мысли разные

формат цепочки арифметического выражения
{
    uint32_t OBJTags::OBJ_RPNChain;
    uint32_t size                   // количество элементов цепочки
    {
        uint32_t OBJTags::OBJ_Node;
        uint32_t type;              // тип элемента
        uint32_t nodeTag;           // тэг типа элемента
        union {
            uint32_t number;        // если элемент число - значение числа
            uint8_t  op;            // если элемент операция - тип операции
            {                       // если элемент метка - имя метки
                uint32_t OBJTags::OBJ_Token;
                uint32_t len                    // Длина строки имени метки
                uint8_t[len] label_text;        // Строка имени метки в unicode
            }
        }
    }[size];
}


т.о. если делать в общем случае, ссылку на метку - ссылкой на арифметическое выражение.
А арифметическое выражение в общем случае может состоять из одного имени или числа.

то можно сделать такую таблицу
{
    uint32_t type;      // тип элемента: константа или подставляемое значение
    uint32_t offset;    // смещение, где встретилось арифметическое выражение
    // для константы - это вычисляемое значение,
    // для подставляемого значения - смещение, куда значение надо подставить.
    {   // для константы - тут сохраняется имя константы
        // для подставляемого значения тут должна быть заглушка или вырожденное значение нулевого размера
        uint32_t OBJTags::OBJ_Token;
        uint32_t len                    // Длина строки имени метки
        uint8_t[len] label_text;        // Строка имени метки в unicode
    }
    
    RPNChain    rpn;    // собственно цепочка арифм. выражения.
    // когда цепочка вырождается в один элемент "число", то она считается разрешённой,
    // число подставляется по заданному значению, и элемент удаляется из таблицы.
    // для константы - константа добавляется в таблицу глобальных меток
}

Вылезла такая проблема:
Если парсить операнд и он будет или число или уже определённая метка, то CalcRPN вычислит результат
и подставит его. А для метки этого делать нельзя, потому что релокация. А определить, что там было уже никак.

А если не вычислять результат парсинга, то будет слишком много не сделано, что можно сделать.
Надо наверное дополнительно анализировать элементы арифметического выражения на наличие имён меток или констант

 
